package cn.iocoder.yudao.module.hs.service.receiveorder;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.framework.datapermission.core.annotation.DataPermission;
import cn.iocoder.yudao.module.hs.controller.admin.receiveorder.vo.ReceiveOrderPageReqVO;
import cn.iocoder.yudao.module.hs.controller.admin.receiveorder.vo.ReceiveOrderSaveReqVO;
import cn.iocoder.yudao.module.hs.controller.admin.receiveorder.vo.ReceiveOrderTrayRespVO;
import cn.iocoder.yudao.module.hs.controller.admin.shiporder.vo.ShipOrderSaveReqVO;
import cn.iocoder.yudao.module.hs.dal.dataobject.picktaskmaterialdetail.PickTaskMaterialDetailDO;
import cn.iocoder.yudao.module.hs.dal.dataobject.receiveorder.ReceiveOrderDO;
import cn.iocoder.yudao.module.hs.dal.dataobject.receiveorderdetail.ReceiveOrderDetailDO;
import cn.iocoder.yudao.module.hs.dal.dataobject.shiporder.ShipOrderDO;
import cn.iocoder.yudao.module.hs.dal.dataobject.warehouse.WarehouseDO;
import cn.iocoder.yudao.module.hs.dal.mysql.receiveorder.ReceiveOrderMapper;
import cn.iocoder.yudao.module.hs.dal.mysql.receiveorderdetail.ReceiveOrderDetailMapper;
import cn.iocoder.yudao.module.hs.dal.redis.RedisKeyConstants;
import cn.iocoder.yudao.module.hs.dal.redis.no.OrderNoRedisDAO;
import cn.iocoder.yudao.module.hs.enums.receiveorder.ReceiveOrderStatus;
import cn.iocoder.yudao.module.hs.enums.receiveorder.ReceiveTypeEnum;
import cn.iocoder.yudao.module.hs.enums.shiporder.ShipOrderStatusEnum;
import cn.iocoder.yudao.module.hs.service.picktaskmaterialdetail.PickTaskMaterialDetailService;
import cn.iocoder.yudao.module.hs.service.shiporder.ShipOrderService;
import cn.iocoder.yudao.module.hs.service.warehouse.WarehouseService;
import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;

import javax.annotation.Resource;
import java.time.LocalDateTime;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;

import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils.getLoginUserId;
import static cn.iocoder.yudao.module.hs.enums.ErrorCodeConstants.*;

/**
 * 收货单 Service 实现类
 *
 * @author 航盛
 */
@Service
@Validated
public class ReceiveOrderServiceImpl implements ReceiveOrderService {

    @Resource
    private ReceiveOrderMapper receiveOrderMapper;
    @Resource
    private ReceiveOrderDetailMapper receiveOrderDetailMapper;
    @Resource
    private ShipOrderService shipOrderService;
    @Resource
    private PickTaskMaterialDetailService pickTaskMaterialDetailService;
    @Resource
    private OrderNoRedisDAO orderNoRedisDAO;
    @Resource
    private AdminUserApi adminUserApi;
    @Resource
    private WarehouseService warehouseService;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void createReceiveOrder(ReceiveOrderSaveReqVO createReqVO) {
        List<String> codes = createReqVO.getCodeList();
        if (codes.size() > 5) {
            throw exception(RECEIVE_ORDER_CODE_LIST_TOO_MANY);
        }
        AdminUserRespDTO user = adminUserApi.getUser(getLoginUserId());
        codes.forEach(code -> {
            // 根据编码分别查询是rfid、物料箱、产品码找到对应的拣货物料
            List<PickTaskMaterialDetailDO> pickTaskMaterialDetailList = pickTaskMaterialDetailService.getPickTaskMaterialDetailByCode(code);
            if (CollectionUtil.isEmpty(pickTaskMaterialDetailList)) {
                throw exception(RECEIVE_ORDER_NOT_MATERIAL, code);
            }
            PickTaskMaterialDetailDO pickTaskMaterialDetail = pickTaskMaterialDetailList.get(0);
            // 出库单
            ShipOrderDO shipOrder = shipOrderService.getShipOrder(pickTaskMaterialDetail.getShipOrderId());
            if (ObjectUtil.isNull(shipOrder)) {
                throw exception(SHIP_ORDER_NOT_EXISTS);
            }
            if (ObjectUtil.notEqual(ShipOrderStatusEnum.TRANSPORTING.getStatus(), shipOrder.getStatus())
                    && ObjectUtil.notEqual(ShipOrderStatusEnum.RECEIVED.getStatus(), shipOrder.getStatus())
                    && ObjectUtil.notEqual(ShipOrderStatusEnum.ENTRYING.getStatus(), shipOrder.getStatus())
                    && ObjectUtil.notEqual(ShipOrderStatusEnum.ENTRY.getStatus(), shipOrder.getStatus())
            ) {
                throw exception(RECEIVE_ORDER_STATUS_ERROR, code);
            }
            if (!user.getWarehouseId().equals(shipOrder.getToWarehouseId())) {
                throw exception(RECEIVE_ORDER_WAREHOUSE_ERROR);
            }
            // 校验是否已经存在收货单
            ReceiveOrderDO receiveOrderDO = receiveOrderMapper.selectOne(ReceiveOrderDO::getShipOrderId,
                    shipOrder.getId());
            if (ObjectUtil.isNull(receiveOrderDO)) {
                // 没有收货单就新增收货单
                receiveOrderDO = buildReceiveOrder(shipOrder.getId(), shipOrder.getCode());
                receiveOrderDO.setQuickEntry(createReqVO.getQuickEntry());
                receiveOrderMapper.insert(receiveOrderDO);
            }
            for (PickTaskMaterialDetailDO pickTaskMaterialDetailDO : pickTaskMaterialDetailList) {
                // 如果收货单是收货中，校验收货单明细是否存在
                ReceiveOrderDetailDO orderDetail = receiveOrderDetailMapper.selectOne(
                        ReceiveOrderDetailDO::getMaterialCode, pickTaskMaterialDetailDO.getMaterialCode());
                if (ObjectUtil.isNotNull(orderDetail)) {
                    // 已存在就跳过不处理
                    continue;
                }
                // 新增收货单明细
                ReceiveOrderDetailDO saveOrderDetailDO = buildReceiveOrderDetail(pickTaskMaterialDetailDO,
                        receiveOrderDO.getId(), receiveOrderDO.getShipOrderId(), createReqVO.getReceiveType(), receiveOrderDO.getWarehouseId());
                receiveOrderDetailMapper.insert(saveOrderDetailDO);
            }
            // 出库的产品码
            List<String> shipMaterialCodes = pickTaskMaterialDetailService
                    .getPickTaskMaterialDetailByShipOrderId(shipOrder.getId())
                    .stream().map(PickTaskMaterialDetailDO::getMaterialCode).collect(Collectors.toList());
            // 收货的产品码
            List<String> receiveMaterialCodes = receiveOrderDetailMapper
                    .selectListByShipOrderId(shipOrder.getId())
                    .stream().map(ReceiveOrderDetailDO::getMaterialCode).collect(Collectors.toList());
            // 校验是否全部收货
            if (CollectionUtils.isEqualCollection(shipMaterialCodes, receiveMaterialCodes)) {
                // 全部收货更新收货单状态为已收货
                receiveOrderDO.setStatus(ReceiveOrderStatus.RECEIVED.getStatus());
                receiveOrderMapper.updateById(receiveOrderDO);
                // 全部收货完成之后，更新出库单状态为已收货
                shipOrder.setStatus(ShipOrderStatusEnum.RECEIVED.getStatus());
                shipOrderService.updateShipOrder(BeanUtils.toBean(shipOrder, ShipOrderSaveReqVO.class));
            }
        });
    }

    /**
     * 构建收货单
     *
     * @param shipOrderId   出库单id
     * @param shipOrderCode 出库单号
     * @return 收货单
     */
    private ReceiveOrderDO buildReceiveOrder(Long shipOrderId, String shipOrderCode) {
        ReceiveOrderDO receiveOrderDO = new ReceiveOrderDO();
        receiveOrderDO.setNo(orderNoRedisDAO.generate(OrderNoRedisDAO.RECEIVE_NO_PREFIX, RedisKeyConstants.RECEIVE_NO));
        receiveOrderDO.setShipOrderId(shipOrderId);
        receiveOrderDO.setShipOrderCode(shipOrderCode);
        AdminUserRespDTO user = adminUserApi.getUser(getLoginUserId());
        if (ObjectUtil.isNotNull(user)) {
            receiveOrderDO.setConsignee(String.valueOf(user.getId()));
            receiveOrderDO.setWarehouseId(user.getWarehouseId());
            WarehouseDO warehouse = warehouseService.getWarehouse(user.getWarehouseId());
            receiveOrderDO.setDeptId(warehouse.getDeptId());
        }
        receiveOrderDO.setReceiveTime(LocalDateTime.now());
        receiveOrderDO.setStatus(ReceiveOrderStatus.RECEIVING.getStatus());
        return receiveOrderDO;
    }

    /**
     * 构建收货单明细
     *
     * @param pickTaskMaterial 拣货任务明细
     * @param receiveOrderId   收货单id
     * @param shipOrderId      出库单id
     * @param receiveType      收货类型
     * @return 收货单明细
     */
    private ReceiveOrderDetailDO buildReceiveOrderDetail(PickTaskMaterialDetailDO pickTaskMaterial, Long receiveOrderId,
                                                         Long shipOrderId, Integer receiveType, Long warehouseId) {
        ReceiveOrderDetailDO saveOrderDetailDO = new ReceiveOrderDetailDO();
        saveOrderDetailDO.setOrderId(receiveOrderId);
        saveOrderDetailDO.setShipOrderId(shipOrderId);
        saveOrderDetailDO.setMaterialCode(pickTaskMaterial.getMaterialCode());
        saveOrderDetailDO.setMaterialName(pickTaskMaterial.getMaterialName());
        saveOrderDetailDO.setModeCode(pickTaskMaterial.getMaterialModeCode());
        saveOrderDetailDO.setBoxCode(pickTaskMaterial.getBoxCode());
        saveOrderDetailDO.setTrayCode(pickTaskMaterial.getTrayCode());
        saveOrderDetailDO.setRfidCode(pickTaskMaterial.getRfidCode());
        saveOrderDetailDO.setReceiveType(receiveType == null ? ReceiveTypeEnum.MANUAL.getType() : receiveType);
        saveOrderDetailDO.setWarehouseId(warehouseId);
        return saveOrderDetailDO;
    }

    @Override
    public void deleteReceiveOrder(Long id) {
        // 校验存在
        validateReceiveOrderExists(id);
        // 删除
        receiveOrderMapper.deleteById(id);
    }

    private void validateReceiveOrderExists(Long id) {
        if (receiveOrderMapper.selectById(id) == null) {
            throw exception(RECEIVE_ORDER_NOT_EXISTS);
        }
    }

    @Override
    public ReceiveOrderDO getReceiveOrder(Long id) {
        return receiveOrderMapper.selectById(id);
    }

    @Override
    public ReceiveOrderDO getReceiveOrderByShipOrderId(Long shipOrderId) {
        return receiveOrderMapper.selectOne(ReceiveOrderDO::getShipOrderId, shipOrderId);
    }

    @Override
    public PageResult<ReceiveOrderDO> getReceiveOrderPage(ReceiveOrderPageReqVO pageReqVO) {
        return receiveOrderMapper.selectPage(pageReqVO);
    }

    @Override
    @DataPermission(enable = false)
    public List<ReceiveOrderTrayRespVO> getTrayList(Long shipOrderId, Long warehouseId) {
        return receiveOrderMapper.getTrayList(shipOrderId, warehouseId);
    }

}